﻿// Collection.cpp : 实现文件
//

#include "stdafx.h"
#include "GHCY.h"
#include "Ground.h"
//#include "../Hui/Hui.h"
//#include "Car.h"
//#include "GException.h"
#include "LogList.h"
#include "CollStarts.h"
#include "Collection.h"
#include "Collections.h"

// Collection

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>同类端点集合类</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明>同类端点集合，用于神经网络计算</说明>
Collections::Collections()
{
	m_arrColls.SetSize(0, 10);
}

Collections::~Collections()
{
	if (!m_bAutoDelete)
		return;

	for (size_t i = 0, length = m_arrColls.GetCount(); i < length; i++)
	{
		delete m_arrColls[i];
	}
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>添加一个集合</功能>
/// <参数 name = "pep">指向端点</参数>
/// <返回></返回>
/// <说明>端点总数</说明>
INT_PTR Collections::add(Collection* pColl)
{
	return m_arrColls.Add(pColl);
}

/// <作者>Mr. Hu</作者>
/// <日期>202001009</日期>
/// <功能>取端点数量</功能>
/// <参数 name = ""></参数>
/// <返回>端点总数</返回>
/// <说明></说明>
INT_PTR Collections::getSize()
{
	return m_arrColls.GetCount();
}

/// <作者>Mr. Hu</作者>
/// <日期>20201009</日期>
/// <功能>初始化神经元</功能>
/// <参数 name = ""></参数>
/// <返回></返回>
/// <说明></说明>
void Collections::initNeural()
{
	m_arrColls.SetSize(0, 8);
	for (size_t i = 0, length = theApp.m_pGround->m_arrEndPoint.GetCount(); i < length; i++)
	{
		EndPoint* pep = theApp.m_pGround->m_arrEndPoint[i];
		Collection* pCollection = 0;

		// bufCode 不同，新加一个集合
		// 只适用数据库中所有相同的bufCode排在一起的情况，不适用原江西重庆道路
		// 20210327 改成bufCode排在一起也能用
		for (size_t j = 0, nColls = m_arrColls.GetCount(); j < nColls; j++)
		{
			Collection* pColl = m_arrColls[j];
			if (*(UINT64*)pep->m_bufCode == *(UINT64*)pColl->m_bufCode)		// 遍历相同的编号
			{
				pCollection = pColl;
				break;
			}
		}

		if (!pCollection)
		{
			switch (pep->m_pointType)
			{
			case POINT_TYPE::POINT_START:		// 发车点
				pCollection = new CollStarts();
				break;
			default:
				pCollection = new Collection();
				break;
			}
			m_arrColls.Add(pCollection);
			*(UINT64*)pCollection->m_bufCode = *(UINT64*)pep->m_bufCode;	// 编号
			pCollection->m_pointType = pep->m_pointType;	// 类型
			// pCollection->m_nSerial = i + 61; 暂时不用了

			// 初始化优先级
			if (pep->isRecharge()) // 充电
				pCollection->m_priority0.p1 = 10;// pep->m_bufCode[1] == L'3' ? 10 : 0;// 11 : 0;	// 在线充电是10，离线或发车充电是0	// 20210516 都改为0，保证坚美雅洁存货后不动	// 20210713 改回10，否则不充电
			else if (pep->isProduct())	// ???? 20201213 产品优先级高二级
				pCollection->m_priority0.p1 = pep->m_bufCode[3];					// 最后一位表示优先级
			else if (pep->isStore())	// ???? 20201213 空托区高一级
				pCollection->m_priority0.p1 = pep->m_bufCode[3];					// 最后一位表示优先级
			else if (pep->isMiddle())	// 中间等待点
				pCollection->m_priority0.p1 = 10;
			else if (pep->isStart())
				pCollection->m_priority0.p1 = 0;
			else if (pep->isWait())
				pCollection->m_priority0.p1 = 5;
			else
				pCollection->m_priority0.p1 = 0;
		}
		ASSERT(pCollection);
		pCollection->add(pep);
		pep->m_pColl = pCollection;
	}

	// 各集合的关系
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection& colli = *m_arrColls[i];
		ASSERT(colli.m_arrPoint.GetCount());
		EndPoint* pep = colli.m_arrPoint[0];
		for (size_t j = 0; j < nColls; j++)
		{
			Collection& collj = *m_arrColls[j];
			if (pep->isParent(collj.m_arrPoint[0]))		// ???? 可能是多对多的关系，只能保留一个？
			{
				//colli.m_pPreColl = &collj;		// 设置对应关系
				//collj.m_pNextColl = &colli;
				colli.m_collsPrevious.add(&collj);
				collj.m_collsNext.add(&colli);
			}
		}
	}
}

/// <作者>Mr. Hu</作者>
/// <日期>20201101</日期>
/// <功能>从集合中判断指定点是不是充电点</功能>
/// <参数 name = "prn">RoadNode * 指定的道路节点</参数>
/// <返回>TRUE 是充电点，</返回>
/// <说明></说明>
EndPoint* Collections::isRecharge(RoadNode* prn)
{
	size_t length = m_arrColls.GetCount();
	for (size_t i = 0; i < length; i++)	// 遍历集合
	{
		Collection& coll = *m_arrColls[i];
		if (coll.m_pointType != POINT_TYPE::POINT_RECHARGE)			// 不是充电集
			continue;					// 跳过

		EndPoint* pep = coll.sameCard(prn);
		if (pep)			// 卡号相同
			return pep;
	}
	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>20240521</日期>
/// <功能>获取充电点集合</功能>
/// <参数 name = ""></参数>
/// <返回>0 无充电点，否则返回充点点集合</返回>
/// <说明></说明>
Collection* Collections::getRecharges()
{
	size_t length = m_arrColls.GetCount();
	for (size_t i = 0; i < length; i++)	// 遍历集合
	{
		Collection& coll = *m_arrColls[i];
		if (coll.m_pointType == POINT_TYPE::POINT_RECHARGE)			// 不是充电集
			return &coll;					// 跳过
	}
	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>20210412</日期>
/// <功能>计算集合优先级</功能>
/// <参数 name = ""></参数>
/// <返回>1 成功分配任务，否则 0</返回>
/// <说明>计算集合优先级，并分配AGV</说明>
bool Collections::updatePriority( Car* pCar )
{
	// 优先回到AGV的起始点取货
	//if (pCar->m_arrPath.GetCount())		// 只用一次，不用锁定
	//{
	//	PathNode& pn = pCar->m_arrPath[0];
	//	//if (pn.m_nPointID)
	//	//{
	//	//	EndPoint* pep = theApp.m_pGround->m_arrEndPoint[pn.m_nPointID - 1];
	//	//	if (pep->isProduct()
	//	//		&& pep->allowPickup(pCar)
	//	//		)
	//	//	{
	//	//		int nGet = pep->getRoadNode()->m_nID - 1;	// 如果是同类，直接导到终点
	//	//		if (theApp.m_pGround->carStartGet(pCar, nGet))
	//	//		{
	//	//			// 设置AGV对应的呼叫点通讯实例
	//	//			//pc0->m_pCallSocket = pep0->m_pSocket;
	//	//		}

	//	//		return;
	//	//	}
	//	//}
	//}
				







	// 重置优先级和最大最小层数等
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->init();

	// 计算取货数量，对下一级的影响
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->init2();

	// 更新优先级，不能与上一句合并，因为可能改变其它的优先级
	// =================================================================
	// 序号	区号	说明
	//	0	R201	在线充电
	//	1	R301	离线充电
	//	2	A101	下砖区
	//	3	1202	存储区
	//	4	A301	两个电梯
	//	5	1601	电梯前等待
	for (size_t i = 0; i < nColls; i++)
		m_arrColls[i]->updatePriority(pCar);

	// 新建优先级表
	CArray<Collection*> colls;
	colls.SetSize(0, nColls);
	// 优先级排降序
	for (size_t i = 0; i < nColls; i++)
	{
		for (size_t j = 0; j <= i; j++)
		{
			if (j == i)
				colls.Add(m_arrColls[i]);	// 添加一项
			else if (m_arrColls[i]->m_priority.p1 > colls[j]->m_priority.p1)
			{
				colls.InsertAt(j, m_arrColls[i]);	// 插到中间
				break;
			}
		}
	}

	// 删除较低优先级
	INT32 p = colls[0]->m_priority.p1;
	if (p <= 0)		// 没有可用点，直接返回
		return 0;

	// 20210722 坚美雅洁专用，注销这一段，不删除级别较低的，因为立库出货时，可能去充电而不是取货
	// 2022 南基项目使用，否则有呼叫也去充电，保留优先级最高的几组
	for (size_t i = 1; i < nColls; i++)
	{
		ASSERT(colls[i]->m_priority.p1 <= p);

		if (colls[i]->m_priority.p1 < p)
			colls[i] = 0;
	}

	// 20201215 注释这一段，因为充电的AGV可能是最近的，	// ??
	// 找最近的AGV
	EndPoint* pep0 = 0;		// 选中的端点
	Car* pc0 = 0;			// 选中的AGV
	INT32 dis0 = MAXINT32;	// 最短距离
	for (size_t i = 0, length = theApp.m_pGround->m_arrCars.GetCount(); i < length; i++)
	{
		Car* pc = theApp.m_pGround->m_arrCars[i];
		if (pc->getOutside())	// 不能用getDisable
			continue;			// 停用，跳过

		//====11 潜伏式，取货也可以再去取货
		//if (pc->m_pReadData->nState == 0x1045)
		//	continue;	// 取货时不再去取货

		//ASSERT(pc->m_pEndPoint);
		//if (!pc->m_pEndPoint)
		//	continue;

		// 20220806 初次使用可能没有初始化
		if (!pc->m_arrPath.GetCount())
			continue;

		if (!pc->waiting()/* && !pc->isInit()*/
			&& pc->m_pEndPoint
			&& !(pc->m_pEndPoint->isRecharge()		// 20220411 非充电，充电过程可重新规划路线
				&& theApp.m_bBreakRecharge
				)
			&& !pc->m_pEndPoint->isRecharge()		// 
			)
		{
			continue;	// 不是等待和初始化，跳过
		}

		// 如果正在充电，并且电量低，跳过
		if (
			pc->m_pEndPoint
			&& pc->m_pEndPoint->isRecharge()
			&& pc->isLowBattery()
			)
		{
			continue;
		}

		// 20210715 坚美雅洁 在A区等待也参与计算
		//if (pc->m_pEndPoint && pc->m_pEndPoint->m_bufCode[0] == L'A')
		//	continue;	// 在Axxx区等待，不参与计算

		if (pc->m_bLoaded)
			continue;	// 拉空托在充电 ????

		for (size_t j = 0; j < nColls && colls[j]; j++)
		{
			if (colls[j]->m_pointType == POINT_TYPE::POINT_START)
				continue;					// 排除发车点

			INT32 dis = 0;
			EndPoint* pep = colls[j]->getPickupPoint(pc, dis);
			if (!pep)
				continue;



			// 20210716 坚美雅洁专用 充电点优先到1号点（A302），没有解决前往充电又呼叫取货的问题，F:\指挥调度系统\GHCY潜伏2T\调度和避让问题.docx\20210716 0603
			//if((pep && *(UINT64*)pep->getCode() == *(UINT64*)L"A302")			// 到1号点，A302
			//	)
			//{
			//	if ((充电AGV == pc)
			//		//&& ((pc->m_pReadData->nCardType1 & 0x00ff) == 0x0011)	// 20201102 在线充电时直接请求运行，推行或拉
			//		//&& (pc->m_pReadData->nDistance >= 1000)				// ？？？？？？？？？？？？？？？？？？？ ?? 20210713 过卡后后下一条，否则不正常。有两处
			//		)
			//	{
			//		dis = 0;			// 正在充电的最优先
			//	}
			//	else if (充电AGV)
			//		continue;			// 如果有准备充电的AGV, 不到1号点
			//}

			// 20250711 不去当前站点
			if (pCar && pCar->m_pEndPoint
				&& pCar->m_pEndPoint == pep
				&& !(pCar->m_pReadData->sd.sd9.nCmd & 0x1000)				// 非初始化
				)
			{
				return 0;
			}

			if (dis < dis0)
			{
				pc0 = pc;
				dis0 = dis;
				pep0 = pep;
			}
		}
	}

	// 开始运行
	if (!pep0)
		return 0;

	// 如果是充电的过程中，重新规划
	if (theApp.m_bBreakRecharge
		&& pc0->m_pEndPoint
		&& pc0->m_pEndPoint->isRecharge()
		)
	{
		// 不成功
		// 20220416 暂时关闭，因为没有解决同一个点走两次的问题
		int nGet = pep0->getRoadNode()->m_nID - 1;		// 如果是同类，直接导到终点
		if (theApp.m_pGround->carStartGet2(pc0, nGet))
		{
			// 保存日志，测试用
#pragma 测试完成后去掉
			GException ge;
			ge.m_nCode = 7004;
			ge.m_strObject = L"carStartGet 2222";
			theApp.m_pLogList->addLog(&ge);

			// 设置AGV对应的呼叫点通讯实例
			//pc0->m_pCallSocket = pep0->m_pSocket;
			return 1;
		}
	}
	// 否则，从起点规划
	else
	{
		int nGet = pep0->getRoadNode()->m_nID - 1;	// 如果是同类，直接导到终点
		if (theApp.m_pGround->carStartGet(pc0, nGet))
		{
			// 保存日志，测试用
#pragma 测试完成后去掉
			GException ge;
			ge.m_nCode = 7003;
			ge.m_strObject = L"carStartGet";
			theApp.m_pLogList->addLog(&ge);

			// 设置AGV对应的呼叫点通讯实例
			//pc0->m_pCallSocket = pep0->m_pSocket;
			return 1;
		}
	}

	return 0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>能否到达指定的点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <参数 name = "nPoint">端点号，id-1</参数>
/// <参数 name = "bFirst">指定终眯集合</参数>
/// <异常 name = "GException">没有找到发车点<异常>
/// <返回>能到TRUE，不能到FALSE</返回>
/// <说明>指定的发车点能否到达指定的端点，如果bFirst==TRUE并且私有终点表有值，用私有终点表判断，否则用公用终点表</说明>
BOOL Collections::isStartFriend(int nSerial, int nPoint, BOOL bFirst)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			if (((CollStarts*)pColl)->isFriend(nSerial, nPoint, bFirst))
			{
				return TRUE;
			}
		}
	}

	// 没有对应的发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误2";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return FALSE;

	// ?? 改变私有点和公有点用法
	// 发车点号，如果bFirst==TRUE并且私有终点表有值，用私有终点表判断，否则用公用终点表
	//CArray<int>& arrPoints = bFirst && m_arrStartPoints[i]->m_arrPoints1.GetCount()
	//	? m_arrStartPoints[i]->m_arrPoints1 : m_arrStartPoints[i]->m_arrPoints2;
	//length = arrPoints.GetCount();
	//i = 0;
	//for (; i < length && arrPoints[i] != nPoint; i++);

	//return i < length;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>取发车点对应的节点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <异常 name = "GException">没有找到发车点<异常>
/// <返回>对应的道路节点，id-1</返回>
/// <说明>根据发车点号查找对应的道路节点(id-1)，如果没有发车点抛出异常</说明>
int Collections::getStartNode2(int nSerial)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			int id = ((CollStarts*)pColl)->getNode2(nSerial);
			if(id)
			{
				return id;
			}
		}
	}

	// 没有找到发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误1";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return  0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190901</日期>
/// <功能>取发车点</功能>
/// <参数 name = "nSerial">发车点号</参数>
/// <返回>对应的发车点指针</返回>
/// <说明>如果没有发车点抛出异常</说明>
EndPoint* Collections::getStartPoint(int nSerial)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		// 类型不匹配，不是发车点
		if (pColl->m_pointType != POINT_TYPE::POINT_START)
		{
			continue;
		}

		// 取发车点
		for (size_t j = 0, length = pColl->m_arrPoint.GetSize(); j < length; j++)
		{
			if (pColl->m_arrPoint[j]->m_nSerial == nSerial)
			{
				return pColl->m_arrPoint[j];
			}
		}
	}

	// 没有找到发车点
	// 保存日志
	GException* pge = new GException;
	pge->m_nCode = 2041;
	pge->m_strSummary = L"发车点错误1";
	pge->m_strDetails.Format(L"AGV发车点 %i 错误。", nSerial);

	THROW(pge);

	//return  0;
}

/// <类型>Fanction</类型>
/// <作者>Mr. Hu</作者>
/// <日期>20190902</日期>
/// <功能>是不是发车点</功能>
/// <参数 name = "nNode">道路节点号id-1</参数>
/// <返回>对应的发车点号，-1没有</返回>
/// <异常 name = ""><异常>
/// <说明>判断给定的道路节点是不是发车点</说明>
int Collections::isStartNode(int nNode)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		if (pColl->m_pointType == POINT_TYPE::POINT_START)	// 发车点集合
		{
			int nStart = ((CollStarts*)pColl)->isStartNode(nNode);
			if (nStart >= 0)
			{
				return nStart;
			}
		}
	}

	return -1;
}

/// <作者>Mr. Hu</作者>
/// <日期>20210413</日期>
/// <功能>取道路节点id对应的端点id</功能>
/// <参数 name = "nType">节点类型</参数>
/// <参数 name = "nNodeID">道路节点号id</参数>
/// <返回>取道路节点id对应的端点id，0没找到</返回>
/// <异常 name = ""><异常>
/// <说明>通过类型和道路ID查找对应的端点，不用道路节点的m_nPoint属性，因为一个节点可能对应多个端点，如充电和发车</说明>
EndPoint* Collections::getPoint(POINT_TYPE nType, int nNodeID)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		// 类型不匹配
		if (pColl->m_pointType != nType)
		{
			continue;
		}

		// 取端点
		for (size_t j = 0, length = pColl->m_arrPoint.GetSize(); j < length; j++)
		{
			if (pColl->m_arrPoint[j]->m_pRoadNode->m_nID == nNodeID)
			{
				return pColl->m_arrPoint[j];
			}
		}
	}

	return 0;
}

/// <作者>Mr. Hu</作者>
/// <日期>20240923</日期>
/// <功能>获取站点编码对应的集合</功能>
/// <参数 name = "bufCode">unicode 字符串，要求4个字，8个字节</参数>
/// <返回>找到的集合，0没找到</返回>
/// <异常 name = ""><异常>
/// <说明></说明>
Collection* Collections::getColl(LPCTSTR bufCode)
{
	// 遍历发车集
	size_t nColls = m_arrColls.GetCount();
	for (size_t i = 0; i < nColls; i++)
	{
		Collection* pColl = m_arrColls[i];
		// 类型不匹配
		if (*(UINT64*)bufCode == *(UINT64*)pColl->m_bufCode)		// 遍历相同的编号
		{
			return pColl;
		}
	}

	return 0;
}

